package com.guli.cart.service.impl;
/*
 * @Author 罗俊
 * @date 2020/11/9 - 11:10 上午
 *
 */

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.guli.cart.feign.AliOssFeignClient;
import com.guli.cart.feign.ProductFeignClient;
import com.guli.cart.interceptor.CartInterceptor;
import com.guli.cart.service.CartService;
import com.guli.common.pojo.vo.CartVo;
import com.guli.cart.vo.SaleAttrVo;
import com.guli.common.pojo.vo.SkuItemVo;
import com.guli.common.pojo.vo.UserInfoVo;
import com.guli.common.constant.CartConstant;
import com.guli.common.pojo.vo.StatusCodeEnum;
import com.guli.common.utils.R;
import com.guli.common.utils.UserInfoVoUtils;
import com.guli.mall.serviceBase.globalException.MallException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import static com.guli.common.pojo.vo.StatusCodeEnum.CART_HAVE_NOT_SKU;
import static com.guli.common.pojo.vo.StatusCodeEnum.SKU_DOES_NOT_EXIST;

@Slf4j
@Service
public class CartServiceImpl implements CartService {

    @Resource
    RedisTemplate<String, SkuItemVo> redisTemplate;

    @Resource
    ProductFeignClient productFeignClient;

    @Resource
    ThreadPoolExecutor threadPoolExecutor;

    @Resource
    AliOssFeignClient aliOssFeignClient;

    private static String ALI_OSS_PREFIX;

    private static final TypeReference<SkuItemVo> SKU_ITEM_VO_TYPE_REFERENCE = new TypeReference<SkuItemVo>() {
    };

    private static final TypeReference<List<SaleAttrVo>> SALE_ATTR_VO_LIST_TYPE_REFERENCE = new TypeReference<List<SaleAttrVo>>() {
    };

    /**
     * @param skuItemVo
     * @param userInfoVo2 如果为 null，代表是主线程（http请求线程）调用此方法，否则，就是线程池中的线程调用此方法
     */
    @Override
    public void addToCart(List<SkuItemVo> skuItemVo, UserInfoVo userInfoVo2) {

        Map<Long, SkuItemVo> skuItemVoMap = skuItemVo.stream().collect(
                Collectors.toMap(SkuItemVo::getSkuId,
                        skuItemVo2 -> {
                            return skuItemVo2;
                        }
                ));
        String userKey = getUserKey(userInfoVo2);


        BoundHashOperations<String, Long, SkuItemVo> boundHashOps = redisTemplate.boundHashOps(userKey);

        boundHashOps.putAll(skuItemVoMap);

        redisTemplate.expire(userKey, CartConstant.USER_KEY_FOR_TEMP_COOKIE_EXPIRE_SECONDS, TimeUnit.SECONDS);

    }

    private String getUserKey(UserInfoVo userInfoVo) {
        //TODO 因为是多线程环境，所以获取不到主线程的用户信息
        UserInfoVo userInfoV2 = ObjectUtils.isEmpty(userInfoVo) ? CartInterceptor.THREAD_LOCAL.get() : userInfoVo;

        String userKey = null;

        if (StringUtils.isEmpty(userInfoV2.getUserId())) {
            // 离线购物车
            userKey = UserInfoVoUtils.getOffLineCartUserKeyOfRedis(userInfoV2);
        } else {
            // 在线购物车
            userKey = UserInfoVoUtils.getOnLineCartUserKeyOfRedis(userInfoV2);
        }

//        userKey = JSON.toJSONString(userKey);
        return userKey;
    }


    @Override
    public SkuItemVo addToCart(Long skuId, Integer number) throws ExecutionException, InterruptedException {

        SkuItemVo skuItemVoAfterPlusOne = ifAlreadyExistThenAddSkuItemCount(skuId, number);

        if (!ObjectUtils.isEmpty(skuItemVoAfterPlusOne)) {

            log.info("该用户购物车中已经有{}，现将其购物车中该商品的数量加{}", skuItemVoAfterPlusOne.getSkuName(), number);

            return skuItemVoAfterPlusOne;
        }


        /**
         * 查询sku信息的任务
         * */
        CompletableFuture<SkuItemVo> querySkuTask = CompletableFuture.supplyAsync(() -> {
            R info = productFeignClient.info(skuId);

            Object skuInfoObj = info.get("skuInfo");

            if (ObjectUtils.isEmpty(skuInfoObj)) {
                // 该 sku 不存在
                throw new MallException(SKU_DOES_NOT_EXIST);
            }

            SkuItemVo skuItemVo = JSON.parseObject(JSON.toJSONString(skuInfoObj), SKU_ITEM_VO_TYPE_REFERENCE);

            return skuItemVo;

        }, threadPoolExecutor);


        /**
         * 查询sku对应的销售属性的任务
         * */
        CompletableFuture<List<SaleAttrVo>> querySaleAttrTask = CompletableFuture.supplyAsync(() -> {
            R result = productFeignClient.getSkuSaleAttrValueBySkuId(skuId);

            List<SaleAttrVo> saleAttrVos = result.getData(SALE_ATTR_VO_LIST_TYPE_REFERENCE);

            return saleAttrVos;

        }, threadPoolExecutor);

        UserInfoVo userInfoVo = CartInterceptor.THREAD_LOCAL.get();

        /**
         * 组合查询到的信息
         * */
        CompletableFuture<SkuItemVo> combineSkuAndAttrTask = querySkuTask.thenCombineAsync(querySaleAttrTask, (skuItemVo, saleAttrVos) -> {

            skuItemVo.setCount(number);

            List<String> saleAttrStrList = saleAttrVos.stream().map(saleAttrVo -> {
                return saleAttrVo.getAttrName() + "：" + saleAttrVo.getAttrValue();
            }).collect(Collectors.toList());

            skuItemVo.setSpuAttr(saleAttrStrList);

            this.addToCart(Collections.singletonList(skuItemVo), userInfoVo);

            return skuItemVo;

        }, threadPoolExecutor);

        CompletableFuture.allOf(querySkuTask, querySaleAttrTask, combineSkuAndAttrTask);

        return combineSkuAndAttrTask.get();
    }

    @Override
    public SkuItemVo getSkuOfCartBySkuId(Long skuId) {

        String userKey = getUserKey(null);

        BoundHashOperations<String, Long, SkuItemVo> hashOps = redisTemplate.boundHashOps(userKey);

        SkuItemVo skuItemVo = hashOps.get(skuId);

        if (ObjectUtils.isEmpty(skuItemVo)) {
            throw new MallException(CART_HAVE_NOT_SKU);
        }

        skuItemVo.setSkuDefaultImg(getAliOssPrefix() + skuItemVo.getSkuDefaultImg());

        return skuItemVo;
    }

    @Override
    public CartVo getCartList() {

        // 如果临时购物车中的数据还没有合并，那就将临时购物车中的数据添加到登陆购物车中
        UserInfoVo userInfoVo = CartInterceptor.THREAD_LOCAL.get();
        mergeOffLineCartToOnLineCart(userInfoVo);

        String userKey = getUserKey(null);

        BoundHashOperations<String, Long, SkuItemVo> hashOps = redisTemplate.boundHashOps(userKey);

        if (CollectionUtils.isEmpty(hashOps.entries())) {
            return new CartVo();
        }
        List<SkuItemVo> collect = new ArrayList<>(hashOps.entries().values());

        collect.forEach(skuItemVo -> {
            skuItemVo.setSkuDefaultImg(getAliOssPrefix() + skuItemVo.getSkuDefaultImg());
        });

        CartVo cartVo = new CartVo();

        cartVo.setItems(collect);

        return cartVo;
    }

    @Override
    public SkuItemVo updateCartByUserKey(SkuItemVo skuItemVo) {

        BoundHashOperations<String, Long, SkuItemVo> hashOps = redisTemplate.boundHashOps(getUserKey(null));

        SkuItemVo itemVo = hashOps.get(skuItemVo.getSkuId());

        if (ObjectUtils.isEmpty(itemVo)) {
            throw new MallException(CART_HAVE_NOT_SKU);
        }

        if (!ObjectUtils.isEmpty(skuItemVo.getCount())) {
            itemVo.setCount(skuItemVo.getCount());
        }

        if (!ObjectUtils.isEmpty(skuItemVo.getIsSelected())) {
            itemVo.setIsSelected(skuItemVo.getIsSelected());
        }
        hashOps.put(skuItemVo.getSkuId(), itemVo);

        return itemVo;
    }

    @Override
    public void deleteItemByUserKey(Long skuId) {
        BoundHashOperations<String, Long, SkuItemVo> hashOps = this.redisTemplate.boundHashOps(getUserKey(null));

        hashOps.delete(skuId);
    }

    @Override
    public CartVo getSelectedItemForPayment(Long memberId) {

        String onLineCartUserKeyOfRedis = UserInfoVoUtils.getOnLineCartUserKeyOfRedis(memberId.toString());

        BoundHashOperations<String, Long, SkuItemVo> hashOps = redisTemplate.boundHashOps(onLineCartUserKeyOfRedis);

        List<SkuItemVo> values = hashOps.values();

        List<SkuItemVo> selectedCartItems = values.stream().filter(skuItemVo -> {
            skuItemVo.setSkuDefaultImg(getAliOssPrefix() + skuItemVo.getSkuDefaultImg());

            /**
             * 要更新价格
             * 否则，如果用户是1年前加入的购物车，价格也是一年前的价格，付款时，需要使用最新的价格
             * */
            R skuSaleAttrValueBySkuId = productFeignClient.getSkuSaleAttrValueBySkuId(skuItemVo.getSkuId());

            Map<String,String> skuInfo = (Map<String,String>)skuSaleAttrValueBySkuId.get("skuInfo");

            if(ObjectUtils.isEmpty(skuInfo)){
                // 购物车缓存中的商品在商品表中查询不到
                throw new MallException(SKU_DOES_NOT_EXIST);
            }

            String price = skuInfo.get("price");

            skuItemVo.setPrice(new BigDecimal(price));

            return skuItemVo.getIsSelected();
        }).collect(Collectors.toList());

        CartVo cartVo = new CartVo();

        cartVo.setItems(selectedCartItems);

        return cartVo;
    }

    /**
     * 如果临时购物车中的数据还没有合并，那就将临时购物车中的数据添加到登陆购物车中
     *
     * @param userInfoVo
     */
    private void mergeOffLineCartToOnLineCart(UserInfoVo userInfoVo) {
        if (ObjectUtils.isEmpty(userInfoVo.getUserId())) {
            // 用户没有登陆，直接返回
            return;
        }

        // 获取离线购物车
        BoundHashOperations<String, Long, SkuItemVo> hashOffLineCartOps = redisTemplate.boundHashOps(UserInfoVoUtils.getOffLineCartUserKeyOfRedis(userInfoVo));

        List<SkuItemVo> values = hashOffLineCartOps.values();

        if (CollectionUtils.isEmpty(values)) {
            // 离线购物车中没有商品，直接返回
            return;
        }

        BoundHashOperations<String, Long, SkuItemVo> hashOnLineOps = redisTemplate.boundHashOps(UserInfoVoUtils.getOnLineCartUserKeyOfRedis(userInfoVo));

        // 将离线购物车中的商品拷贝到在线购物车中
        hashOffLineCartOps.entries().entrySet().parallelStream().forEach(longSkuItemVoEntry -> {
            hashOnLineOps.put(longSkuItemVoEntry.getKey(), longSkuItemVoEntry.getValue());
        });

        // 清空离线购物车
        // hashOffLineCartOps.delete(hashOffLineCartOps.keys());
        redisTemplate.delete(UserInfoVoUtils.getOffLineCartUserKeyOfRedis(userInfoVo));

    }

    private SkuItemVo ifAlreadyExistThenAddSkuItemCount(Long skuId, Integer number) {

        String userKey = getUserKey(null);

        BoundHashOperations<String, Long, SkuItemVo> boundHashOps = redisTemplate.boundHashOps(userKey);

        SkuItemVo skuItemVo = boundHashOps.get(skuId);

        if (!ObjectUtils.isEmpty(skuItemVo)) {
            // 购物车中已经有该商品，只需要将该商品的数量+1即可
            //SkuItemVo skuItemVo = JSON.parseObject(skuItemVoStr, SKU_ITEM_VO_TYPE_REFERENCE);

            skuItemVo.setCount(skuItemVo.getCount() + number);

            boundHashOps.put(skuId, skuItemVo);

            return skuItemVo;
        }

        return null;
    }

    private static Lock lock = new ReentrantLock();

    public String getAliOssPrefix() {

        if(StringUtils.isEmpty(ALI_OSS_PREFIX)){

            lock.lock();
            try {

                while (StringUtils.isEmpty(ALI_OSS_PREFIX)){
                    afterPropertiesSet();
                    TimeUnit.SECONDS.sleep(5);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        return ALI_OSS_PREFIX;
    }

    public void afterPropertiesSet() {

        R ossHost = aliOssFeignClient.getOssHost();

        ALI_OSS_PREFIX = ossHost.get("host").toString() + "/";
    }
}
